﻿using System;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using UnityEditor;
using UnityEngine;
using YooAsset.Editor.AssetBundleOdinEditor;

namespace YooAsset.Editor
{
    public class AssetBundleCollector
    {
        /// <summary>
        /// 收集路径
        /// 注意：支持文件夹或单个资源文件
        /// </summary>
        [InlineButton("SelectFolder","Open")]
        public string CollectPath = string.Empty;

        /// <summary>
        /// 打包规则类名
        /// </summary>
        [Header("打包规则")]
        [DisplayAsString]
        public string PackRuleName = "PackSeparately";

        [OnValueChanged("PackRuleChange")]
        public PackRule PackRule = PackRule.PackSeparately;
        /// <summary>
        /// 过滤规则类名
        /// </summary>
        [Header("过滤规则")]
        [DisplayAsString]
        public string FilterRuleName = "CollectAll";

        [OnValueChanged("FilterRuleChange")]
        public FilterRule FilterRule = FilterRule.CollectAll;
        /// <summary>
        /// 不写入资源列表
        /// </summary>
        [Header("是否写入资源列表")]
        public bool NotWriteToAssetList = false;

        /// <summary>
        /// 资源分类标签
        /// </summary>
        [Header("资源分类")]
        public string AssetTags = string.Empty;


        /// <summary>
        /// 检测配置错误
        /// </summary>
        public void CheckConfigError()
        {
            if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(CollectPath) == null)
                throw new Exception($"Invalid collect path : {CollectPath}");

            if (AssetBundleGrouperSettingData.HasPackRuleName(PackRuleName) == false)
                throw new Exception($"Invalid {nameof(IPackRule)} class type : {PackRuleName}");

            if (AssetBundleGrouperSettingData.HasFilterRuleName(FilterRuleName) == false)
                throw new Exception($"Invalid {nameof(IFilterRule)} class type : {FilterRuleName}");
        }

        /// <summary>
        /// 获取打包收集的资源文件
        /// </summary>
        public List<CollectAssetInfo> GetAllCollectAssets(AssetBundleGrouper grouper)
        {
            Dictionary<string, CollectAssetInfo> result = new Dictionary<string, CollectAssetInfo>(1000);
            bool isRawAsset = PackRuleName == nameof(PackRawFile);

            // 如果是文件夹
            if (AssetDatabase.IsValidFolder(CollectPath))
            {
                string collectDirectory = CollectPath;
                string[] findAssets = EditorTools.FindAssets(EAssetSearchType.All, collectDirectory);
                foreach (string assetPath in findAssets)
                {
                    if (IsValidateAsset(assetPath) == false)
                        continue;
                    if (IsCollectAsset(assetPath) == false)
                        continue;
                    if (result.ContainsKey(assetPath) == false)
                    {
                        string bundleName = GetBundleName(grouper, assetPath);
                        List<string> assetTags = GetAssetTags(grouper);
                        var collectAssetInfo = new CollectAssetInfo(bundleName, assetPath, assetTags, isRawAsset, NotWriteToAssetList);
                        collectAssetInfo.DependAssets = GetAllDependencies(assetPath);
                        result.Add(assetPath, collectAssetInfo);
                    }
                    else
                    {
                        throw new Exception($"The collecting asset file is existed : {assetPath} in collector : {CollectPath}");
                    }
                }
            }
            else
            {
                string assetPath = CollectPath;
                if (result.ContainsKey(assetPath) == false)
                {
                    if (isRawAsset && NotWriteToAssetList)
                        UnityEngine.Debug.LogWarning($"Are you sure raw file are not write to asset list : {assetPath}");

                    string bundleName = GetBundleName(grouper, assetPath);
                    List<string> assetTags = GetAssetTags(grouper);
                    var collectAssetInfo = new CollectAssetInfo(bundleName, assetPath, assetTags, isRawAsset, NotWriteToAssetList);
                    result.Add(assetPath, collectAssetInfo);
                }
                else
                {
                    throw new Exception($"The collecting asset file is existed : {assetPath} in collector : {CollectPath}");
                }
            }

            // 返回列表
            return result.Values.ToList();
        }

        private bool IsValidateAsset(string assetPath)
        {
            if (assetPath.StartsWith("Assets/") == false && assetPath.StartsWith("Packages/") == false)
                return false;

            if (AssetDatabase.IsValidFolder(assetPath))
                return false;

            // 注意：忽略编辑器下的类型资源
            Type type = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
            if (type == typeof(LightingDataAsset))
                return false;

            string ext = System.IO.Path.GetExtension(assetPath);
            if (ext == "" || ext == ".dll" || ext == ".cs" || ext == ".js" || ext == ".boo" || ext == ".meta")
                return false;

            return true;
        }

        private bool IsCollectAsset(string assetPath)
        {
            // 如果收集全路径着色器
            if (AssetBundleGrouperSettingData.Setting.AutoCollectShaders)
            {
                Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
                if (assetType == typeof(UnityEngine.Shader))
                    return true;
            }

            // 根据规则设置过滤资源文件
            IFilterRule filterRuleInstance = AssetBundleGrouperSettingData.GetFilterRuleInstance(FilterRuleName);
            return filterRuleInstance.IsCollectAsset(new FilterRuleData(assetPath));
        }

        private string GetBundleName(AssetBundleGrouper grouper, string assetPath)
        {
            // 如果收集全路径着色器
            if (AssetBundleGrouperSettingData.Setting.AutoCollectShaders)
            {
                System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
                if (assetType == typeof(UnityEngine.Shader))
                {
                    return RevisedBundleName(AssetBundleGrouperSettingData.Setting.ShadersBundleName);
                }
            }

            // 根据规则设置获取资源包名称
            IPackRule packRuleInstance = AssetBundleGrouperSettingData.GetPackRuleInstance(PackRuleName);
            string bundleName = packRuleInstance.GetBundleName(new PackRuleData(assetPath, CollectPath, grouper.GrouperName));
            return RevisedBundleName(bundleName);
        }

        private List<string> GetAssetTags(AssetBundleGrouper grouper)
        {
            List<string> tags = StringUtility.StringToStringList(grouper.AssetTags, ';');
            List<string> temper = StringUtility.StringToStringList(AssetTags, ';');
            tags.AddRange(temper);
            return tags;
        }

        private List<string> GetAllDependencies(string mainAssetPath)
        {
            List<string> result = new List<string>();
            string[] depends = AssetDatabase.GetDependencies(mainAssetPath, true);
            foreach (string assetPath in depends)
            {
                if (IsValidateAsset(assetPath))
                {
                    // 注意：排除主资源对象
                    if (assetPath != mainAssetPath)
                        result.Add(assetPath);
                }
            }

            return result;
        }


        /// <summary>
        /// 修正资源包名
        /// </summary>
        public static string RevisedBundleName(string bundleName)
        {
            return EditorTools.GetRegularPath(bundleName).ToLower();
        }
        
        private void SelectFolder()
        {
            string dataPath = Application.dataPath;
            string selectedPath = EditorUtility.OpenFolderPanel("Path", dataPath, "");
            if (!string.IsNullOrEmpty(selectedPath))
            {
                if (selectedPath.StartsWith(dataPath))
                {
                    CollectPath = "Assets/" + selectedPath.Substring(dataPath.Length + 1);
                }
                else
                {
                    CollectPath = "";
                    Debug.LogError(new GUIContent("Can not be outside of 'Assets/'!"));
                }
            }
        }

        private void FilterRuleChange()
        {
            switch (FilterRule)
            {
                case FilterRule.CollectAll:
                    FilterRuleName = "CollectAll";
                    break;
                case FilterRule.CollectScene:
                    FilterRuleName = "CollectScene";
                    break;
                case FilterRule.CollectPrefab:
                    FilterRuleName = "CollectPrefab";
                    break;
                case FilterRule.CollectSprite:
                    FilterRuleName = "CollectSprite";
                    break;
            }
        }

        private void PackRuleChange()
        {
            switch (PackRule)
            {
                case PackRule.PackSeparately:
                    PackRuleName = "PackSeparately";
                    break;
                case PackRule.PackDirectory:
                    PackRuleName = "PackDirectory";
                    break;
                case PackRule.PackCollector:
                    PackRuleName = "PackCollector";
                    break;
                case PackRule.PackGrouper:
                    PackRuleName = "PackGrouper";
                    break;
                case PackRule.PackRawFile:
                    PackRuleName = "PackRawFile";
                    break;
            }
        }
    }
}